package rest

import (
	"net/http"
	"reflect"
	"strings"
	"encoding/json"
	"container/list"
	"../dog"
)

type OptionsModel struct {
	url_model
	Options []string
}

func Init() *http.ServeMux {
	controller_list := list.List{}

	dog_keeper := dog.NewDogKeeper()
	dog_keeper.DeSerialize()

	var api_controller APIController
	controller_list.PushBack(&api_controller)

	var dogscontroller DogsController
	dogscontroller.dogkeeper = dog_keeper
	controller_list.PushBack(&dogscontroller)

	var dogcontroller DogController
	dogcontroller.dogkeeper = dog_keeper
	controller_list.PushBack(&dogcontroller)

	var lastwordcontroller LastWordController
	lastwordcontroller.lastword_factory = dog.GetDefaultLastWordFactory()
	controller_list.PushBack(&lastwordcontroller)

	var dognamescontroller DogNamesController
	dognamescontroller.dogfactory = dog.GetDefaultDogFactory()
	controller_list.PushBack(&dognamescontroller)

	mux := http.NewServeMux()

	for item_ele := controller_list.Front(); item_ele != nil; item_ele = item_ele.Next() {
		rest_controller, success := item_ele.Value.(RestFulController)
		if success {
			initMux(mux, rest_controller)

			if rest_controller.IsRoot() {
				api_controller.api_models = append(api_controller.api_models, OptionsModel{
					url_model: url_model{
						Url: rest_controller.GetSourceName(),
					},
					Options: getRestFulControllerOptions(rest_controller),
				})
			}
		}
	}

	return mux
}

func getRestFulControllerOptions(controller interface{}) []string {
	var controller_val reflect.Value

	if reflect.ValueOf(controller).Kind() == reflect.Ptr {
		controller_val = reflect.ValueOf(controller)
	} else {
		controller_val = reflect.ValueOf(&controller)
	}
	//_, opt_success := controller_val.Interface().(OptionsAPIFunc)
	_, get_success := controller_val.Interface().(GetAPIFunc)
	_, post_success := controller_val.Interface().(PostAPIFunc)
	_, put_success := controller_val.Interface().(PutAPIFunc)
	_, del_success := controller_val.Interface().(DeleteAPIFunc)
	var options []string

	//if opt_success {
	options = append(options, "OPTIONS")
	//}

	if get_success {
		options = append(options, "GET")
	}

	if post_success {
		options = append(options, "POST")
	}

	if put_success {
		options = append(options, "PUT")
	}

	if del_success {
		options = append(options, "DELETE")
	}

	return options
}

func initMux(mux *http.ServeMux, controller RestFulController) {
	controller_val := reflect.ValueOf(controller)
	//opt_controller, opt_success := controller_val.Interface().(OptionsAPIFunc)
	get_controller, get_success := controller_val.Interface().(GetAPIFunc)
	post_controller, post_success := controller_val.Interface().(PostAPIFunc)
	put_controller, put_success := controller_val.Interface().(PutAPIFunc)
	del_controller, del_success := controller_val.Interface().(DeleteAPIFunc)

	mux.HandleFunc(controller.GetSourceName(), func(writer http.ResponseWriter, request *http.Request) {
		var back_obj interface{}
		switch strings.ToUpper(request.Method) {
		case "OPTIONS":
			//if opt_success {
			//	back_obj = opt_controller.Options(request)
			//}
			back_obj = OptionsModel{
				url_model: url_model{
					Url: controller.GetSourceName(),
				},
				Options: getRestFulControllerOptions(controller),
			}

		case "GET":
			if get_success {
				back_obj = get_controller.Get(request)
			} else {
				back_obj = Error(controller, "Unsupported httpmethod")
			}
		case "POST":
			if post_success {
				back_obj = post_controller.Post(request)
			} else {
				back_obj = Error(controller, "Unsupported httpmethod")
			}
		case "PUT":
			if put_success {
				back_obj = put_controller.Put(request)
			} else {
				back_obj = Error(controller, "Unsupported httpmethod")
			}
		case "DELETE":
			if del_success {
				back_obj = del_controller.Delete(request)
			} else {
				back_obj = Error(controller, "Unsupported httpmethod")
			}
		default:
			back_obj = Error(controller, "Unsupported httpmethod")
		}

		json_obj, err := json.Marshal(back_obj)
		if err != nil {
			writer.WriteHeader(http.StatusInternalServerError)
			writer.Header().Set("content-type", "text/html")
			writer.Write([]byte(err.Error()))
		} else {
			writer.Header().Set("content-type", "text/json")
			writer.Write([]byte(json_obj))
		}
	})
}
